﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace _01_TCP服务端
{
    public partial class Form1 : Form
    {
        //创建服务器的Socket对象
        Socket socketServer = null;
        //创建一个字典用来存储连接过来的客户端
        Dictionary<string, Socket> dicClient = new Dictionary<string, Socket>();

        public Form1()
        {
            InitializeComponent();
            //允许跨线程访问控件
            Control.CheckForIllegalCrossThreadCalls = false;
        }

        /// <summary>
        /// 打开服务器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            //第一步：创建Socket对象
            socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                //第二步：设置服务器的IP地址和端口号
                socketServer.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Parse(this.textBox1.Text), int.Parse(this.textBox2.Text)));
                //第三步：设置最大连接数
                socketServer.Listen(10);
                //第四步：创建线程用以监听连接过来的客户端
                Task.Run(CheckListening);
                //第五步：提示服务器启动成功
                this.richTextBox1.SelectionColor = Color.Green;
                this.richTextBox1.AppendText("服务器启动成功！\n");
                //第六步：禁用按钮
                this.button1.Enabled = false;
            }
            catch (Exception ex)
            {
                //提示错误信息
                this.richTextBox1.SelectionColor = Color.Red;
                this.richTextBox1.AppendText("服务器启动失败！:"+ ex.Message +"\n");
            }
        }
        /// <summary>
        /// 监听连接过来的客户端
        /// </summary>
        public void CheckListening()
        {
            while (true)
            {
                //调用Accept方法，会阻塞当前线程，直到有客户端连接过来才会解除阻塞继续执行
                Socket socketClient = socketServer.Accept();
                //获取客户端的IP地址和端口号
                string strClient = socketClient.RemoteEndPoint.ToString();
                //将连接过来的客户端添加到字典中
                dicClient.Add(strClient, socketClient);
                //添加下拉框中
                this.comboBox1.Items.Add(strClient);
                //修改显示颜色，提示连接成功
                this.richTextBox1.SelectionColor = Color.Green;
                this.richTextBox1.AppendText("客户端连接成功！" + strClient + "\n");
                //创建线程用以接收客户端发送过来的数据
                Task.Run(() => ReceiveMessage(socketClient));

            }
        }
        /// <summary>
        /// 接收客户端发送过来的数据
        /// </summary>
        /// <param name="socketClient"></param>
        public void ReceiveMessage(Socket socketClient)
        {
            while (true)
            {
                try
                {
                    //创建一个字节数组用来存储接收的数据
                    byte[] buffer = new byte[1024 * 1024*2];
                    //接收数据
                    int length = socketClient.Receive(buffer);
                    //提取第一个字节，判断数据类型
                    InfoType infoType = (InfoType)buffer[0];
                    //根据不同的数据类型进行处理
                    switch (infoType)
                    {
                        case InfoType.None:
                            break;
                        case InfoType.HeartBeat:
                            break;
                        case InfoType.Message:
                            //将接收到的数据转换成字符串
                            string strMessage = Encoding.Default.GetString(buffer, 1, length);
                            //修改显示颜色，提示接收到数据
                            this.richTextBox1.SelectionColor = Color.Blue;
                            this.richTextBox1.AppendText("接收到数据：" + strMessage + "\n");
                            break;
                        case InfoType.JsonMessage:
                            break;
                        default:
                            break;
                    }
                }
                catch (Exception ex)
                {
                    //将客户端从字典中移除
                    dicClient.Remove(socketClient.RemoteEndPoint.ToString());
                    //将客户端从下拉框中移除
                    this.comboBox1.Items.Remove(socketClient.RemoteEndPoint.ToString());
                    //提示客户端断开连接
                    this.richTextBox1.SelectionColor = Color.Red;
                    this.richTextBox1.AppendText("客户端断开连接！" + socketClient.RemoteEndPoint.ToString() + "\n");
                    break;
                }

            }
        }
        /// <summary>
        /// 停止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            //关闭服务器
            socketServer?.Close(); //关闭服务器的Socket对象
            //提示关闭服务器
            this.richTextBox1.SelectionColor = Color.Red;
            this.richTextBox1.AppendText("服务器关闭成功！\n");
        }
        /// <summary>
        /// 发送-消息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button3_Click(object sender, EventArgs e)
        {
            //获取下拉框中选中的客户端
            string strClient = this.comboBox1.SelectedItem.ToString();
            //获取下拉框中选中的客户端的Socket对象
            Socket socketClient = dicClient[strClient];
            //获取要发送的消息，转换成byte数组
            byte[] buffer = Encoding.Default.GetBytes(this.textBox3.Text);
            //创建新的byte数组，长度为要发送的消息长度+1
            byte[] newBuffer = new byte[buffer.Length + 1];
            //将消息类型存储到新数组的第一个字节中
            newBuffer[0] = (byte)InfoType.Message;
            //将要发送的消息存储到新数组中
            //Array.Copy方法将要发送的消息复制到新数组中
            //参数一：源数组
            //参数二：源数组的起始位置
            //参数三：目标数组
            //参数四：目标数组的起始位置
            //参数五：要复制的长度
            Array.Copy(buffer, 0, newBuffer, 1, buffer.Length);
            //发送消息
            socketClient.Send(newBuffer);

        }

        /// <summary>
        /// 定时器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            //定时器每隔3秒发送一次心跳包，向所有连接过来的客户端发送心跳包
            foreach (var item in dicClient)
            {
                try
                {
                    //获取客户端的Socket对象
                    Socket socketClient = item.Value;
                    //创建新的byte数组，长度为1
                    byte[] buffer = new byte[1];
                    //将消息类型存储到新数组的第一个字节中
                    buffer[0] = (byte)InfoType.HeartBeat;
                    //发送心跳包
                    socketClient.Send(buffer);
                }
                catch (Exception ex)
                {
                    //将客户端从字典中移除
                    dicClient.Remove(item.Key);
                    //将客户端从下拉框中移除
                    this.comboBox1.Items.Remove(item.Key);
                    //提示客户端断开连接
                    this.richTextBox1.SelectionColor = Color.Red;
                    this.richTextBox1.AppendText("客户端异常断开连接！" + item.Key + "\n");
                }


            }
        }
    }
}
